package com.rizzo.web.backingbeans.common;

import com.rizzo.back.exception.ApplicationException;
import com.rizzo.back.exception.ApplicationExceptionItem;
import com.rizzo.web.util.MessageUtil;
import org.springframework.binding.message.MessageBuilder;
import org.springframework.binding.message.MessageContext;
import org.springframework.context.NoSuchMessageException;
import org.springframework.webflow.execution.FlowExecutionException;

import java.io.Serializable;

/**
 * @author Jan Lievens
 */
public class ParentBackingBean implements Serializable {

    /**
     * Will transform the {@link org.springframework.webflow.execution.FlowExecutionException} into
     * readable exceptions for showing purposes.
     *
     * @param messageContext         The {@link org.springframework.binding.message.MessageContext} to put messages on.
     * @param flowExecutionException The {@link org.springframework.webflow.execution.FlowExecutionException} where the exception resides.
     */
    public void transformException(MessageContext messageContext, FlowExecutionException flowExecutionException) {
        // log the error in the log-file
        ApplicationException applicationException = findApplicationException(flowExecutionException);
        if (null != applicationException) {
            // put every ApplicationExceptionItem on the MessageContext
            for (ApplicationExceptionItem applicationExceptionItem : applicationException.getApplicationExceptionItems()) {
                messageContext.addMessage(new MessageBuilder().fatal().code(
                        applicationExceptionItem.getMsgKey()).args(applicationExceptionItem.getMsgParams()).build());
            }
        } else {
            // peel the FlowExecutionException and search the cause Exception message as key property for the resourceMessage or set system error
            String resourceBundleValue = findResourceBundleValue(flowExecutionException);
            if (resourceBundleValue != null) {
                messageContext.addMessage(new MessageBuilder().error().defaultText(resourceBundleValue)
                        .build());
            } else {
                try {
                    Object[] args = new Object[]{System.identityHashCode(flowExecutionException)};
                    messageContext.addMessage(new MessageBuilder().error().defaultText(MessageUtil.resolveFlowMessage("system.error.args", args))
                            .build());
                } catch (NoSuchMessageException e) {
                    messageContext.addMessage(new MessageBuilder().error().defaultText(MessageUtil.resolveFlowMessage("Generic failure. Please provide a specific message."))
                            .build());
                }
            }
        }
    }

    /**
     * Run over the messages (key) of the causes and search for a value in the bundle.
     *
     * @param flowExecutionException
     * @return
     */
    private String findResourceBundleValue(FlowExecutionException flowExecutionException) {
        Throwable cause = flowExecutionException.getCause();
        String possibleMessage = null;
        while (cause != null) {
            try {
                possibleMessage = MessageUtil.resolveFlowMessage(cause.getMessage());
            } catch (Exception e) {
                // simply do nothing, this happens because we can encounter Strings not defined in the bundles which is unrepairable
            }
            if (possibleMessage != null && !"".equals(possibleMessage)) {
                return possibleMessage;
            }
            cause = cause.getCause();
        }
        return null;
    }

    /**
     * Unravels the FlowExecutionException to find the ApplicationException.
     *
     * @param flowExecutionException The {@link org.springframework.webflow.execution.FlowExecutionException} to unravel.
     * @return A possible {@link ApplicationException}.
     */
    private ApplicationException findApplicationException(FlowExecutionException flowExecutionException) {
        Throwable cause = flowExecutionException.getCause();
        while (cause != null) {
            if (cause instanceof ApplicationException) {
                return (ApplicationException) cause;
            }
            cause = cause.getCause();
        }
        return null;
    }

}
